package com.zmn.plat.business.impl.product;

import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.utils.collection.CollectionUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.plat.business.interfaces.channel.ChannelCategBService;
import com.zmn.plat.business.interfaces.channel.ChannelServiceCategoryBService;
import com.zmn.plat.business.interfaces.pilot.ChannelPriceBService;
import com.zmn.plat.business.interfaces.product.ProductPriceBService;
import com.zmn.plat.business.interfaces.product.ServProductBService;
import com.zmn.plat.business.interfaces.product.tariff.TariffProductBService;
import com.zmn.plat.common.constant.OnePriceConsts;
import com.zmn.plat.common.constant.ProductConsts;
import com.zmn.plat.common.dictionary.PlatErrorCodeDict;
import com.zmn.plat.common.dto.product.ProductPriceDIO;
import com.zmn.plat.common.dto.product.item.ItemDRO;
import com.zmn.plat.common.dto.product.price.ProductPriceDRO;
import com.zmn.plat.common.exception.PlatException;
import com.zmn.plat.manager.dictionary.ProductDict;
import com.zmn.plat.manager.interfaces.cache.AreaCache;
import com.zmn.plat.model.dto.product.ProductRelationDTO;
import com.zmn.plat.model.dto.product.ProductTariffRelationDTO;
import com.zmn.plat.model.dto.service.ItemPriceCalDTO;
import com.zmn.plat.model.entity.area.BaseArea;
import com.zmn.plat.model.entity.category.ServCategAndCategTwoQuery;
import com.zmn.plat.model.entity.channel.ChannelFaultPriceVO;
import com.zmn.plat.model.entity.pilot.SupportPilotVO;
import com.zmn.plat.model.entity.product.ServProduct;
import com.zmn.plat.model.entity.product.ServProductQuery;
import com.zmn.plat.model.vo.product.tariff.item.price.ProductServiceItemExcutePriceResultVo;
import com.zmn.plat.services.interfaces.channel.tariff.ChannelTariffService;
import com.zmn.plat.services.interfaces.product.ServProductService;
import com.zmn.plat.services.interfaces.product.tariff.ProductTariffService;
import com.zmn.plat.utils.bean.BeanCopyUtils;
import com.zmn.plat.utils.bean.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author tanbiao
 * @description
 * @modifier
 * @since 2021/3/16 15:02
 **/
@Service
@Slf4j
public class ProductPriceBServiceImpl implements ProductPriceBService {
    @Resource
    AreaCache areaCache;
    @Resource
    ServProductBService servProductBService;
    @Resource
    ServProductService servProductService;
    @Resource
    ChannelPriceBService channelPriceBService;
    @Resource
    TariffProductBService tariffProductBService;
    @Resource
    ChannelServiceCategoryBService channelServiceCategoryBService;
    @Resource
    ChannelTariffService channelTariffService;
    @Resource
    ProductTariffService productTariffService;
    @Resource
    ChannelCategBService channelCategBService;

    // 分批次处理的数量
    private final static int DEFAULT_PAGE_SIZE = 20;
    // 开启的线程池核心线程数
    private final static int CORE_POOL_SIZE = 4;
    // 开启的线程池最大线程数
    private final static int MAX_POOL_SIZE = 5;
    // 开启的线程池闲置时间
    private final static int KEEP_ALIVE_TIME = 3000;

    @Override
    public List<ProductPriceDRO> listProductPriceByDIO(ProductPriceDIO dio) {
        List<Integer> productIdList = dio.getProductIdList();
        Integer channelId = dio.getChannelId();
        Integer cityId = dio.getCityId();
        Integer showType = dio.getShowType();
        Integer bizType = dio.getBizType();
        if (CollectionUtils.isEmpty(productIdList) || Objects.isNull(channelId) || Objects.isNull(cityId) || Objects.isNull(showType) || Objects.isNull(bizType)) {
            log.error("参数不合法：productIdList={}，channelId={},cityId={},showType={},bizType={}", productIdList, channelId, cityId, showType, bizType);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_PANAM_NULL);
        }
        // 区域数据
        BaseArea baseArea = areaCache.getArea(cityId);
        if (Objects.isNull(baseArea)) {
            log.error("城市对应的数据不存在.cityId={},errorCode:{}", cityId, PlatErrorCodeDict.ERROR_CODE_PRODUCT_PRODUCT_NO_AREA);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_PRODUCT_PRODUCT_NO_AREA);
        }

        // 判断渠道是否开通分类，如果已开通则取当前渠道，反之取上级渠道信息
        Integer avaliableChannelId = channelId;
        try {
            avaliableChannelId = channelServiceCategoryBService.getAvailableCategoryChannelId(channelId);
            log.info("原渠道：{}，可用渠道：{}", channelId, avaliableChannelId);
            dio.setChannelId(avaliableChannelId);
        } catch (PlatException e) {
            log.error("无可用渠道，全部30元起");
            return createNotSupportOnePriceDRO(new HashSet<>(dio.getProductIdList()), channelId, dio.getCityId());
        }

        // 渠道试点设置的定金数据
        SupportPilotVO dto = new SupportPilotVO();
        Integer earnestPrice = 0;
        List<ServCategAndCategTwoQuery> servCategIdList = new ArrayList<>();

        ServProductQuery servProductQuery = new ServProductQuery();
        servProductQuery.setProductIds(productIdList);
        List<ServProduct> servProducts = servProductService.listByQuery(servProductQuery);
        for (ServProduct servProduct : servProducts) {
            SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE, servProduct.getServCategId(), servProduct.getCategId());
            if (Objects.nonNull(supportPilotVO) && NumberUtil.isNotNullOrZero(supportPilotVO.getEarnestPrice()) && !CollectionUtils.isEmpty(supportPilotVO.getServCategIdList())) {
                earnestPrice = supportPilotVO.getEarnestPrice();
                servCategIdList.addAll(supportPilotVO.getServCategIdList());
            }
        }
        if (CollectionUtils.isEmpty(servCategIdList)) {
            dto = null;
        } else {
            dto.setPayType(OnePriceConsts.ONE_PRICE);
            dto.setEarnestPrice(earnestPrice);
            dto.setServCategIdList(servCategIdList);
        }
        log.info("SupportDepositCategoryVO dto:{}", dto);

        // 试点数据提前，用于查询支持试点所有数据
        List<ServCategAndCategTwoQuery> supportList = Objects.equals(dio.getIsTariffChannel(), true) ? Collections.emptyList()
                : Optional.ofNullable(channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.TARIFF, null, null)).orElse(new SupportPilotVO()).getServCategIdList();
        if (Objects.isNull(supportList)) {
            supportList = Collections.emptyList();
        }
        log.info("List<SupportTariffCategoryVO> supportList:{}", supportList);

        // 开启多线程分批次处理产品，产品超过20的分批次处理
        if (dio.getProductIdList().size() <= DEFAULT_PAGE_SIZE) {
            return doExcuteHander(dio, dto, supportList, channelId);
        }
        return handeByThread(dio, dto, supportList, channelId);
    }

    private List<ProductPriceDRO> handeByThread(ProductPriceDIO dio, SupportPilotVO dto, List<ServCategAndCategTwoQuery> supportList, Integer channelId) {

        // 处理完毕之后的数据
        List<ProductPriceDRO> droList = new ArrayList<>();
        // 待处理的数据分批次处理
        List<ProductPriceDIO> dioList = new ArrayList<>();
        // 内存分页，构造分批次的数据
        int totalCount = dio.getProductIdList().size();
        int page = totalCount % DEFAULT_PAGE_SIZE == 0 ? totalCount / DEFAULT_PAGE_SIZE : totalCount / DEFAULT_PAGE_SIZE + 1;
        for (int i = 1; i <= page; i++) {
            ProductPriceDIO sub = new ProductPriceDIO();
            BeanCopyUtils.copyProperties(dio, sub);
            sub.setProductIdList(PageUtils.listPage(dio.getProductIdList(), i, DEFAULT_PAGE_SIZE));
            dioList.add(sub);
        }

        // 开启多线程，以当前处理的页数为队列长度
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(page));
        CompletableFuture<List<ProductPriceDRO>>[] completableFutures = null;
        try {
            completableFutures = dioList.stream().map(excuteDIO -> CompletableFuture.supplyAsync(() -> doExcuteHander(excuteDIO, dto, supportList, channelId), threadPoolExecutor)).toArray(CompletableFuture[]::new);
            // 等待所有任务执行完
            CompletableFuture.allOf(completableFutures).join();
            // 归并返回值
            for (CompletableFuture<List<ProductPriceDRO>> completableFuture : completableFutures) {
                droList.addAll(completableFuture.get());
            }
        } catch (Exception e) {
            log.error("get result from thread pool error", e);
            e.printStackTrace();
        } finally {
            // 关闭线程池
            threadPoolExecutor.shutdown();
        }

        return droList;
    }

    private List<ProductPriceDRO> doExcuteHander(ProductPriceDIO dio, SupportPilotVO dto, List<ServCategAndCategTwoQuery> supportList, Integer channelId) {

        List<Integer> productIdList = dio.getProductIdList();
        Integer showType = dio.getShowType();
        Boolean isTariffChannel = dio.getIsTariffChannel();
        List<ProductPriceDRO> droList = new ArrayList<>(productIdList.size());
        // 渠道配置了定金，检查产品和分类的对应关系是否一致
        List<Integer> handledProductIds = new ArrayList<>();

        // 判断默认产品是否满足定金
        if (Objects.nonNull(dto) && CollectionUtils.isNotEmpty(dto.getServCategIdList())) {
            defaultProduct(handledProductIds, productIdList, dto, droList);
        }
        if (CollectionUtil.isNullOrEmpty(productIdList)) {
            log.info("所有产品均满足定金，不做后续处理");
            return droList;
        }
        // 满足定金的产品数据
        List<ProductRelationDTO> relationDTOList = servProductBService.listProductRalationByIdListAndShowType(productIdList, showType);
        log.debug("List<ProductRelationDTO> relationDTOList:{}", relationDTOList);
        Map<Integer, ProductRelationDTO> relationDTOMap = relationDTOList.stream().collect(Collectors.toMap(e -> e.getProductId(), e -> e, (p, v) -> p));

        // 渠道配置了定金，检查产品和分类的对应关系是否一致
        if (Objects.nonNull(dto) && CollectionUtils.isNotEmpty(dto.getServCategIdList())) {
            handleEarnestPrice(handledProductIds, dto, relationDTOList, droList);
        }
        // 排除掉定金的产品数据
        relationDTOList = relationDTOList.stream().filter(e -> !handledProductIds.contains(e.getProductId())).collect(Collectors.toList());
        // 产品全部满足定金，不走后续操作
        if (CollectionUtils.isEmpty(relationDTOList)) {
            log.info("所有产品均满足定金，不做后续处理");
            return droList;
        }

        // 筛选出后台产品前台产品状态合法的数据(前后台状态都是启用的)
        handleProductStatus(relationDTOList, droList, channelId, dio.getCityId());

        // 查询渠道后端产品
        ServProductQuery query = new ServProductQuery();
        query.setStatus(GlobalConsts.YES);
        query.setChannelId(dio.getChannelId());
        query.setShowType(ProductConsts.ERP_PRODUCT_TYPE);
        query.setProductType(ProductDict.CHANNEL_PRODUCT);
        List<Integer> channelProductIds = Optional.ofNullable(servProductService.listByQuery(query))
                .orElse(Collections.emptyList())
                .stream()
                .map(ServProduct::getProductId)
                .collect(Collectors.toList());

        // 不管是服务项还是计价器路径只要不满足前后台产品之间的一对一关系，30元起
        handleProductOneToOneRelation(relationDTOList, droList, channelProductIds, channelId, dio.getCityId());

        log.debug("List<ProductRelationDTO> relationDTOList:{}", relationDTOList);
        log.debug("List<ProductPriceDRO> droList:{}", droList);

        // 不存在满足条件的数据
        if (CollectionUtils.isEmpty(relationDTOList)) {
            log.info("处理一对一关系结束，均不满足一对一或者状态有问题");
            return droList;
        }

        // 走计价器的数据集合
        List<ProductRelationDTO> supportTariffProducts = new ArrayList<>(relationDTOList.size());
        if (Objects.equals(isTariffChannel, true)) {
            supportTariffProducts = relationDTOList;
        } else {
            // 如果不是默认计价器渠道，校验试点配置中该渠道该城市是否支持计价器
            // 筛选产品中支持计价器试点的所有产品
            Map<String, ServCategAndCategTwoQuery> map = supportList.stream().collect(Collectors.toMap(item -> item.getServCategId() + "_" + item.getCategOneId() + "_" + item.getCategTwoId(), item -> item, (oldVal, currVal) -> oldVal));
            supportTariffProducts = relationDTOList.stream().filter(item -> Objects.nonNull(map.get(item.getShowServCategId() + "_" + item.getShowCategOneId() + "_" + item.getShowCategId()))).collect(Collectors.toList());
        }
        // 走计价器价格表
        droList.addAll(handleSupportByTariffType(supportTariffProducts, relationDTOMap, dio, ProductDict.TRIFF_TYPE_TARIFF, channelId));
        log.debug("List<ProductPriceDRO> droList:{}", droList);

        // 筛选出走服务项的关系数据
        List<Integer> resultProductIds = droList.stream().map(e -> e.getProductId()).collect(Collectors.toList());
        relationDTOList = relationDTOList.stream().filter(e -> !resultProductIds.contains(e.getCurrentProductId())).collect(Collectors.toList());

        // 处理完毕之后剩下的数据需要走服务项价格表
        if (CollectionUtils.isNotEmpty(relationDTOList)) {
            log.info("最后一部分数据走服务价格或30元起");
            // 走服务项价格表
            droList.addAll(handleSupportByTariffType(relationDTOList, relationDTOMap, dio, ProductDict.TRIFF_TYPE_SERVICE, channelId));
        }

        // 最后处理没有价格表的数据为30元起
        List<Integer> droProductIdList = droList.stream().map(e -> e.getProductId()).collect(Collectors.toList());
        Set<Integer> set = dio.getProductIdList().stream().filter(e -> !droProductIdList.contains(e)).collect(Collectors.toSet());
        droList.addAll(createNotSupportOnePriceDRO(set, channelId, dio.getCityId()));
        return droList;
    }

    private void defaultProduct(List<Integer> handledProductIds, List<Integer> productIdList, SupportPilotVO dto, List<ProductPriceDRO> droList) {
        // 判断默认产品是否设置了定金
        ServProductQuery servProductQuery = new ServProductQuery();
        servProductQuery.setProductIds(productIdList);
        servProductQuery.setDefaultType(GlobalConsts.YES);
        List<ServProduct> productList = servProductService.listByQuery(servProductQuery);
        List<Integer> hasEarnestPriceList = new ArrayList<>();
        // 将服务品类拼接
        List<String> collect = dto.getServCategIdList().stream()
                .map(m -> m.getServCategId() + "-" + m.getCategOneId() + "-" + m.getCategTwoId())
                .collect(Collectors.toList());
        // 过滤掉满足条件的数据
        List<ProductPriceDRO> channelDeposits = productList.stream()
                .filter(e -> collect.contains(e.getServCategId() + "-" + e.getCategOneId() + "-" + e.getCategId()))
                .map(e -> {
                    Integer currentProductId = e.getProductId();
                    handledProductIds.add(currentProductId);
                    productIdList.remove(currentProductId);
                    // 原始产品id才是需要最终返回的dro的产品id
                    if (!hasEarnestPriceList.contains(currentProductId)) {
                        hasEarnestPriceList.add(currentProductId);
                        ProductPriceDRO productPriceDRO = new ProductPriceDRO();
                        productPriceDRO.setProductId(currentProductId);
                        productPriceDRO.setPayType(dto.getPayType());
                        productPriceDRO.setFixedPrice(GlobalConsts.YES);
                        productPriceDRO.setEarnestPrice(dto.getEarnestPrice());
                        return productPriceDRO;
                    }
                    return null;
                }).filter(e -> Objects.nonNull(e)).collect(Collectors.toList());
        droList.addAll(channelDeposits);
    }

    /**
     * 描述：查询产品价格表默认项目的价格，需要区分价格表类型
     *
     * @return java.util.List<com.zmn.plat.common.dto.product.price.ProductPriceDRO>
     * @author tanbiao
     * @modifier
     * @since 2021/3/19 18:08
     */
    private List<ProductPriceDRO> handleSupportByTariffType(List<ProductRelationDTO> supportTariffProducts, Map<Integer, ProductRelationDTO> relationDTOMap,
                                                            ProductPriceDIO dio, Integer tariffType, Integer channelId) {
        if (CollectionUtils.isEmpty(supportTariffProducts)) {
            log.info("handleSupportByTariffType supportTariffProducts is empty");
            return Collections.emptyList();
        }

        List<ProductPriceDRO> droList = new ArrayList<>(supportTariffProducts.size());
        //是否开通计价器状态，并且有对应开通上架的计价器产品，并且有对应的开通的前台产品
        supportTariffProducts = supportTariffProducts.stream().filter(e -> filterStatus(e, tariffType)).collect(Collectors.toList());

        // 计价器产品需要额外检查计价器产品和后台产品的一对一关系
        if (Objects.equals(ProductConsts.ET_PRODUCT_TYPE, dio.getShowType())) {
            supportTariffProducts = supportTariffProducts.stream().collect(Collectors.groupingBy(ProductRelationDTO::getTariffProductId))
                    .values().stream().filter(e -> e.size() == 1).flatMap(Collection::stream).collect(Collectors.toList());
        }
        log.debug("List<ProductRelationDTO> supportTariffProducts:{}, tariffType:{}", supportTariffProducts, tariffType);

        // 后台产品对应的价格表
        List<ProductTariffRelationDTO> list = listProductTariffRelation(dio.getChannelId(), dio.getCityId(), dio.getBizType(), tariffType, supportTariffProducts);
        log.debug("List<ProductTariffRelationDTO> list:{}, tariffType:{}", list, tariffType);

        // 查询产品对应的默认服务项并计算价格
        ItemPriceCalDTO dto = new ItemPriceCalDTO(dio.getChannelId(), dio.getCityId(), dio.getBrandId(), dio.getBizType(), null);
        List<ProductServiceItemExcutePriceResultVo> defaultItemPriceList = tariffProductBService.listDefaultItemPriceByRelationDTOList(list, dto, dio.getShowType());
        log.debug("List<ProductServiceItemExcutePriceResultVo> defaultItemPriceList:{}, tariffType:{}", defaultItemPriceList, tariffType);
        // 默认项目的价格
        List<ProductPriceDRO> tariffSupportProducts = defaultItemPriceList.stream().map(e -> setFixedPriceMsg(e, relationDTOMap.get(e.getProductId()), dio.getShowType(), channelId, dio.getCityId())).collect(Collectors.toList());
        droList.addAll(tariffSupportProducts);
        log.debug("List<ProductPriceDRO> droList:{}, tariffType:{}", droList, tariffType);

        // list中不在defaultItemPriceList这一部分产品处理为“不支持一口价”
        List<Integer> supportTariffProductIdList = defaultItemPriceList.stream().map(e -> e.getProductId()).collect(Collectors.toList());
        List<Integer> allRelationTariffProductIdList = list.stream().map(e -> e.getProductId()).collect(Collectors.toList());
        Set<Integer> set = allRelationTariffProductIdList.stream().filter(e -> !supportTariffProductIdList.contains(e)).map(e -> relationDTOMap.get(e).getCurrentProductId()).collect(Collectors.toSet());
        droList.addAll(createNotSupportOnePriceDRO(set, channelId, dio.getCityId()));
        log.debug("List<ProductPriceDRO> droList:{}, tariffType:{}", droList, tariffType);

        return droList;
    }

    /**
     * 描述：处理渠道定金的数据
     *
     * @return java.util.List<com.zmn.plat.common.dto.product.price.ProductPriceDRO>
     * @author tanbiao
     * @modifier
     * @since 2021/3/19 18:08
     */
    private void handleEarnestPrice(List<Integer> handledProductIds, SupportPilotVO dto, List<ProductRelationDTO> relationDTOList, List<ProductPriceDRO> droList) {
        List<Integer> hasEarnestPriceList = new ArrayList<>();
        // 将服务品类拼接
        List<String> collect = dto.getServCategIdList().stream()
                .map(m -> m.getServCategId() + "-" + m.getCategOneId() + "-" + m.getCategTwoId())
                .collect(Collectors.toList());
        // 过滤掉满足条件的数据
        List<ProductPriceDRO> channelDeposits = relationDTOList.stream().filter(e -> collect.contains(e.getShowServCategId() + "-" + e.getShowCategOneId() + "-" + e.getShowCategId())).map(e -> {
            handledProductIds.add(e.getProductId());
            Integer currentProductId = e.getCurrentProductId();
            // 原始产品id才是需要最终返回的dro的产品id
            if (!hasEarnestPriceList.contains(currentProductId)) {
                hasEarnestPriceList.add(currentProductId);
                ProductPriceDRO productPriceDRO = new ProductPriceDRO();
                productPriceDRO.setProductId(currentProductId);
                productPriceDRO.setPayType(dto.getPayType());
                productPriceDRO.setFixedPrice(GlobalConsts.YES);
                productPriceDRO.setEarnestPrice(dto.getEarnestPrice());
                return productPriceDRO;
            }
            return null;
        }).filter(e -> Objects.nonNull(e)).collect(Collectors.toList());
        droList.addAll(channelDeposits);
    }

    /**
     * 描述：处理前后台产品一对一的关系，此处不考虑计价器类型（计价器类型多考虑了一个计价器产品的状态）
     *
     * @return java.util.List<com.zmn.plat.common.dto.product.price.ProductPriceDRO>
     * @author tanbiao
     * @modifier
     * @since 2021/3/19 18:08
     */
    private void handleProductOneToOneRelation(List<ProductRelationDTO> orgDTOList, List<ProductPriceDRO> droList,
                                               List<Integer> channelProductIds, Integer channelId,  Integer cityId) {

        if (CollectionUtils.isEmpty(orgDTOList)) {
            return;
        }
        // 前后台产品一对一关系的数据
        List<ProductRelationDTO> oneToOneList = orgDTOList.stream().collect(Collectors.groupingBy(ProductRelationDTO::getShowProductId))
                .values().stream()
                .filter(e -> {

                    // 渠道产品
                    List<ProductRelationDTO> collect1 = e.stream()
                            .filter(m -> ProductDict.CHANNEL_PRODUCT == m.getProductType())
                            .filter(m -> channelProductIds.contains(m.getProductId()))
                            .collect(Collectors.toList());
                    if (collect1.size() > 1) {
                        return false;
                    } else if (collect1.size() == 1) {
                        e.clear();
                        e.add(collect1.get(0));
                        return true;
                    }

                    // 通用产品
                    List<ProductRelationDTO> collect2 = e.stream()
                            .filter(m -> ProductDict.CURRENCY_PRODUCT == m.getProductType())
                            .collect(Collectors.toList());
                    if (collect2.size() > 1) {
                        return false;
                    } else if (collect2.size() == 1) {
                        e.clear();
                        e.add(collect2.get(0));
                        return true;
                    }

                    return false;

                })
                .flatMap(Collection::stream).collect(Collectors.toList());

        // 剩下的不是一对一的返回三十元起
        List<Integer> oneToOneProductIDList =
                oneToOneList.stream().map(e -> e.getCurrentProductId()).distinct().collect(Collectors.toList());
        List<Integer> allProductIDList =
                orgDTOList.stream().map(e -> e.getCurrentProductId()).distinct().collect(Collectors.toList());
        Set<Integer> notSupportList =
                allProductIDList.stream().filter(e -> !oneToOneProductIDList.contains(e)).collect(Collectors.toSet());
        // 不满足一对一关系的，30元起
        if (CollectionUtils.isNotEmpty(notSupportList)) {
            droList.addAll(createNotSupportOnePriceDRO(notSupportList, channelId, cityId));
        }
        orgDTOList.clear();
        orgDTOList.addAll(oneToOneList);
    }

    /**
     * 描述：处理前后台产品状态数据，需要考虑前后台产品状态，如果存在后台产品不合法，返回三十元起
     *
     * @return java.util.List<com.zmn.plat.common.dto.product.price.ProductPriceDRO>
     * @author tanbiao
     * @modifier
     * @since 2021/3/19 18:08
     */
    private void handleProductStatus(List<ProductRelationDTO> orgDTOList, List<ProductPriceDRO> droList, Integer channelId, Integer cityId) {

        if (CollectionUtils.isEmpty(orgDTOList)) {
            return;
        }
        // 将数据按照状态进行分组处理，如果可用和不可用的存在差异，说明有部分产品是不满足一口价的
        List<ProductRelationDTO> enableStatusRelationList = new ArrayList<>();
        Set<Integer> enableCurrentProductIds = new HashSet<>();
        Set<Integer> disableCurrentProductIds = new HashSet<>();
        for (ProductRelationDTO dto : orgDTOList) {
            // 前后台产品状态必须为开启才能执行一口价判断
            if (Objects.equals(dto.getStatus(), GlobalConsts.YES) && Objects.equals(dto.getShowStatus(),
                                                                                    GlobalConsts.YES)) {
                enableStatusRelationList.add(dto);
                enableCurrentProductIds.add(dto.getCurrentProductId());
            } else {
                disableCurrentProductIds.add(dto.getCurrentProductId());
            }
        }

        if (CollectionUtils.isNotEmpty(disableCurrentProductIds)) {
            // 取差集，如果存在数据，说明这一部分数据全部是不满足一口价产品状态的，返回三十元起
            disableCurrentProductIds.removeAll(enableCurrentProductIds);
            //  三十元起
            List<ProductPriceDRO> list = createNotSupportOnePriceDRO(disableCurrentProductIds, channelId, cityId);
            droList.addAll(list);
        }

        orgDTOList.clear();
        orgDTOList.addAll(enableStatusRelationList);
    }

    /**
     * 描述：返回30元起的实体集合
     *
     * @return java.util.List<com.zmn.plat.common.dto.product.price.ProductPriceDRO>
     * @author tanbiao
     * @modifier
     * @since 2021/3/19 18:08
     */
    private List<ProductPriceDRO> createNotSupportOnePriceDRO(Set<Integer> productIds, Integer channelId,
                                                              Integer cityId) {

        if (CollectionUtils.isNotEmpty(productIds)) {
            Map<Integer, ServProduct> productMap = Optional.ofNullable(
                    servProductService.listByIdList(new ArrayList<>(productIds)))
                                                           .orElse(Collections.emptyList())
                                                           .stream()
                                                           .collect(Collectors.toMap(ServProduct::getProductId,
                                                                                     Function.identity()));
            return productIds.stream().map(e -> {

                SupportPilotVO dto = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE
                        , productMap.get(e).getServCategId(), productMap.get(e).getCategId());
                ProductPriceDRO dro = new ProductPriceDRO();
                dro.setProductId(e);
                dro.setFixedPrice(GlobalConsts.NO);
                dro.setPayType(Objects.nonNull(dto) ? dto.getPayType() : null);
                return dro;
            }).collect(Collectors.toList());
        }
        return Lists.newArrayList();
    }

    /**
     * 设置一口价信息
     *
     * @param productFaultItemExcutePriceResultVo 产品默认服务项
     * @return productPriceDRO 产品一口价信息
     */
    private ProductPriceDRO setFixedPriceMsg(ProductServiceItemExcutePriceResultVo productFaultItemExcutePriceResultVo,
                                             ProductRelationDTO relationDTO, Integer showType, Integer channelId, Integer cityId) {
        ProductPriceDRO productPriceDRO = new ProductPriceDRO();
        productPriceDRO.setProductId(relationDTO.getCurrentProductId());
        productPriceDRO.setPrice(productFaultItemExcutePriceResultVo.getPrice());
        productPriceDRO.setFixedPrice(GlobalConsts.NO);
        ItemDRO itemDRO = new ItemDRO();
        if (Objects.equals(ProductDict.EC_PRODUCT_TYPE, showType)) {
            productFaultItemExcutePriceResultVo.setProductName(relationDTO.getShowProductName());
            productFaultItemExcutePriceResultVo.setServCategName(relationDTO.getShowServCategName());
            productFaultItemExcutePriceResultVo.setCategOneName(relationDTO.getShowCategOneName());
            productFaultItemExcutePriceResultVo.setCategName(relationDTO.getShowCategName());
        } else if (Objects.equals(ProductDict.ERP_PRODUCT_TYPE, showType)) {
            productFaultItemExcutePriceResultVo.setCategOneName(relationDTO.getCategOneName());
            productFaultItemExcutePriceResultVo.setCategName(relationDTO.getCategName());
        }
        BeanUtils.copyProperties(productFaultItemExcutePriceResultVo, itemDRO);
        productPriceDRO.setItemDRO(itemDRO);

        productPriceDRO.setItemId(productFaultItemExcutePriceResultVo.getItemId());
        productPriceDRO.setExternalSettlementPrice(productFaultItemExcutePriceResultVo.getExternalSettlementPrice());
        productPriceDRO.setInternalSettlementPrice(productFaultItemExcutePriceResultVo.getInternalSettlementPrice());

        // 支付方式
        SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE, relationDTO.getShowServCategId(), relationDTO.getShowCategId());
        if (Objects.nonNull(supportPilotVO)) {
            productPriceDRO.setPayType(supportPilotVO.getPayType());
        }

        return productPriceDRO;
    }

    /**
     * 后台产品和价格表的对应关系
     *
     * @param channelId  渠道id
     * @param bizType    业务类型
     * @param tariffType 计价器类型（服务项，计价器）
     * @return productPriceDRO 产品一口价信息
     */
    private List<ProductTariffRelationDTO> listProductTariffRelation(Integer channelId, Integer cityId, Integer bizType, Integer tariffType, List<ProductRelationDTO> dtoList) {
        if (CollectionUtils.isEmpty(dtoList)) {
            log.info("listProductTariffRelation dtoList is empty");
            return Lists.newArrayList();
        }
        // 如果渠道和服务分类不为空，需要查询渠道开通的二级分类
        List<ServCategAndCategTwoQuery> servCategAndCategTwoQueryList =
                channelCategBService.listServCategAndCategTwoQueryByChannelIdAndShowType(channelId, cityId, ProductConsts.ERP_PRODUCT_TYPE);

        if (CollectionUtils.isEmpty(servCategAndCategTwoQueryList)) {
            log.info("listProductTariffRelation 渠道开通的分类为空。");
            return Lists.newArrayList();
        }
        List<Integer> productIds = dtoList.stream().map(e -> e.getProductId()).collect(Collectors.toList());
        Integer tariffId = getChannelDefaultTariff(channelId, bizType, tariffType);
        log.info("listProductTariffRelation tariffId is {},productIds:{}", tariffId, productIds);
        List<ProductTariffRelationDTO> list = null;
        // 1 渠道价格表不存在，那么走产品的默认价格表
        if (!NumberUtil.isPositiveInteger(tariffId)) {
            list = productTariffService.listByProductIdsAndBizTypeAndGeneral(productIds, channelId, bizType, GlobalConsts.YES, tariffType, servCategAndCategTwoQueryList);
        }
        // 2.渠道价格表存在，但是部分产品在该价格表下没有项目，那么此时走产品的默认价格表
        else {
            //价格表对应的哪一部分产品价格表数据
            list = productTariffService.listByProductIdsAndBizTypeAndTariffId(productIds, channelId, bizType, tariffId, servCategAndCategTwoQueryList);
            if (CollectionUtils.isEmpty(list)) {
                list = productTariffService.listByProductIdsAndBizTypeAndGeneral(productIds, channelId, bizType, GlobalConsts.YES, tariffType, servCategAndCategTwoQueryList);
            } else if (list.size() != productIds.size()) {
                // 可能存在部分还需要走默认价格表的数据
                List<Integer> subList = list.stream().map(e -> e.getProductId()).collect(Collectors.toList());
                productIds = productIds.stream().filter(e -> !subList.contains(e)).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(productIds)) {
                    List<ProductTariffRelationDTO> defaultList = productTariffService.listByProductIdsAndBizTypeAndGeneral(productIds, channelId, bizType, GlobalConsts.YES, tariffType, servCategAndCategTwoQueryList);
                    if (CollectionUtils.isNotEmpty(defaultList)) {
                        list.addAll(defaultList);
                    }
                }
            }
        }
        return list;
    }

    /**
     * 查询渠道的价格表
     *
     * @param channelId  渠道id
     * @param bizType    业务类型
     * @param tariffType 计价器类型（服务项，计价器）
     * @return Integer 价格表id
     */
    private Integer getChannelDefaultTariff(Integer channelId, Integer bizType, Integer tariffType) {

        // 查询渠道配置的价格表
        ChannelFaultPriceVO channelFault = channelTariffService.findByChannelIdAndBizTypeAndTariffType(channelId, bizType, tariffType);
        // 存在价格表，并且状态必须为开启
        return Objects.nonNull(channelFault) && Objects.equals(channelFault.getStatus(), GlobalConsts.YES) ? channelFault.getTariffId() : null;
    }


    /**
     * 状态的过滤，如果为计价器产品需要额外添加过滤条件
     *
     * @param dto        产品关系数据
     * @param tariffType 计价器类型（服务项，计价器）
     * @return Integer 价格表id
     */
    private boolean filterStatus(ProductRelationDTO dto, Integer tariffType) {
        boolean result = Objects.equals(dto.getStatus(), GlobalConsts.YES)
                && Objects.equals(dto.getShowStatus(), GlobalConsts.YES);
        // 计价器产品需要额外添加过滤条件
        if (Objects.equals(tariffType, ProductDict.TRIFF_TYPE_TARIFF)) {
            result = result && Objects.equals(dto.getTariffProductStatus(), GlobalConsts.YES)
                    && Objects.equals(dto.getTariffStatus(), GlobalConsts.YES);
        }
        return result;
    }


}
